﻿#Область ОбластьПараметров
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////


// Параметры процедуры обновления
var now = new Date()
var outFileName = 'log' + now.valueOf() + '.txt' // Путь к log-файлу

var TempCatalog = [КаталогВременныхФайлов] // каталог для копирования .cd файла при доп. копировании.
var v8exe = [ИмяИсполняемогоФайлаПрограммы] // Путь к исполняемому файлу 1С:Предприятия 8
var infoBasePath = [ПараметрПутиКИнформационнойБазе]
var BaseFileName = [СтрокаПутиКФайлуИнформационнойБазы]
var connectionString = [СтрокаСоединенияИнформационнойБазы] + '; UC=[КодРазблокировки]'
var adminName = [ИмяАдминистратора] // имя администратора, инициировавшего резервное копирование
var backupFileName = [ФайлРезервнойКопии] // Файл резервной копии
var eventLogID = [СобытиеЖурналаРегистрации]
var comConnectorName = [ИмяCOMСоединителя] // имя COM-класса для работы с 1С:Предприятием 8 через COM-соединение
var useComConnector = [ИспользоватьCOMСоединитель] // флаг использования COM-соединения для работы с 1С:Предприятием 8
var tempLogFileName = 'templog.txt'
var retryCount = 0
var runAppAdditionalParams = [ПараметрыЗапускаПредприятия] // Аргументы командной строки, которые следует добавить к запуску 1С:Предприятия в режиме клиента.
#КонецОбласти

#Область ОбластьРезервногоКопирования

if (useComConnector) {
  var comConnectorCached = new ActiveXObject(comConnectorName) // для защиты от перерегистрации comConnector из других сеансов
}

// Инициализация
var oFileSystemObject = new ActiveXObject('Scripting.FileSystemObject')

var thisFileName
var thisFileDir
var InfoBasePassword

thisFileName = eval('oHTA.commandLine')
thisFileDir = thisFileName.substr(1, thisFileName.lastIndexOf('\\') - 1)
InfoBasePassword = thisFileName.substr(thisFileName.indexOf('[p1]') + 4, thisFileName.indexOf('[/p1]') - thisFileName.indexOf('[p1]') - 4)
InfoBasePassword = fromUnicode(InfoBasePassword)
thisFileName = thisFileName.substr(1, thisFileName.indexOf('[p1]') - 3)

var infoBaseAuthorization = format('/N"{0}" /P"{1}" /WA-', adminName, InfoBasePassword)
connectionString = format(connectionString, adminName, InfoBasePassword)

var oShell = new ActiveXObject('WScript.Shell')
var oShellApplication = new ActiveXObject('Shell.Application')
oShell.CurrentDirectory = thisFileDir

var errorMarker = '{ERR}'
var successMarker = '{OK }'

// Переменные состояния
var logging1C = false
var ComConnection = null

// Открыть файл sFilePath.
function runApp (sFilePath, sFileArgs, show, bWaitOnReturn) {
  if (bWaitOnReturn === undefined) {
    bWaitOnReturn = false
  }
  if (show === undefined) {
    show = SW_SHOW
  }
  if (sFileArgs === undefined) {
    sFileArgs = ''
  }
  var ret = -1
  log(format('[СообщениеНачалоЗапуска]',
    sFilePath, sFileArgs, SWtoString(show), bWaitOnReturn))
  if (oFileSystemObject.FileExists(sFilePath)) {
    try {
      ret = oShell.Run(format('"{0}" {1}', sFilePath, sFileArgs), show, bWaitOnReturn)
    } catch (e) {
      log(format('[СообщениеДеталиИсключения]', e.name, e.message), true)
      return -1
    }
    log(format('[СообщениеРезультатЗапуска]', ret), ret !== 0)
    return ret
  } else {
    log(format("[СообщениеОтказЗапуска]", sFilePath), true)
  }
  return ret
}

function clearLogFile () {
  var outFile = oFileSystemObject.OpenTextFile(outFileName, ForWriting, true, TristateTrue)
  outFile.Close()
}

// Записать текст в лог
function log (text, failed) {
  if (failed === undefined) {
    failed = false
  }
  logFile(text, failed)
}

// Записать текст в лог-файл
function logFile (text, failed) {
  var now = new Date()
  var f
  try {
    f = oFileSystemObject.OpenTextFile(outFileName, ForAppending, true, TristateTrue)
  } catch (e) { return }
  try {
    var status = (failed === false ? successMarker : errorMarker)
    f.WriteLine(format('{0} {1} {2}', now, status, text))
  } finally {
    try {
      f.Close()
    } catch (e) { }
  }
}

// Записать текст из временного лог-файла
function appendLog () {
  var f
  var outf
  var text
  try {
    f = oFileSystemObject.OpenTextFile(tempLogFileName, ForReading, false, TristateFalse)
    outf = oFileSystemObject.OpenTextFile(outFileName, ForAppending, true, TristateTrue)
  } catch (e) { return }
  try {
    var oldAtEndOfStream = f.AtEndOfStream
    if (!oldAtEndOfStream) {
      text = f.ReadAll()
      outf.WriteLine(text)
    }
  } finally {
    try {
      f.Close()
      outf.Close()
    } catch (e) { }
  }
}

// Записать текст в журнал регистрации
function log1C (text, failed) {
  if (logging1C) {
    return
  }
  var connection = createConnection()
  if (connection == null) {
    return
  }
  try {
    log1CInternal(connection, text, failed)
  } finally {
    connection = null
  }
}

// Записать текст в журнал регистрации
function log1CInternal (connection, text, failed) {
  if (logging1C) {
    return
  }
  logging1C = true
  try {
    try {
      var eventLogLevel = failed ? connection.EventLogLevel.Error : connection.EventLogLevel.Information
      connection.WriteLogEvent(eventLogID, eventLogLevel, null, null, text,
        connection.EventLogEntryTransactionMode.Independent)
    } catch (e) {
      log(format('[СообщениеЛогирование1С]', e.name, e.message), true)
      return
    }
  } finally {
    logging1C = false
  }
}

// Инициализация
function initialize () {
  clearLogFile()
  appendLog()
  log(format('[СообщениеПутьКФайлуСкрипта]', thisFileName))
  return 0
}

// Финализация
function finalize (success) {
  if (success === undefined) {
    success = false
  }

  // Запись результата обновления в Event Log
  writeEventLog(success)

  if (!success) {
    allowConnections() // Разрешение подключений
  }

  write1CEventLog() // Запись всей информации из log-файла в журнал регистрации
  setResult(success)

  // очистка глобального COM-соединения
  ComConnection = null
}

function createConnection () {
  if (!useComConnector) {
    return null
  }

  if (ComConnection != null) {
    return ComConnection
  }

  try {
    log('[СообщениеНачалоСеансаСоединенияСБазой]', false)
    var logstep = 'new ActiveXObject COMConnector'
    var ComConnector = new ActiveXObject(comConnectorName)
    logstep = 'comConnector.Connect'
    ComConnection = ComConnector.Connect(connectionString)
    logstep = 'new SystemInfo'
    var systemInfo = ComConnection.NewObject('SystemInfo')
    logstep = 'SystemInfo.AppVersion'
    var appVersion = systemInfo.AppVersion
    logstep = 'SystemInfo.PlatformType'
    var platformType = systemInfo.PlatformType
    logstep = 'Проверка ComConnection.PlatformType'
    var platformTypeString = '[СообщениеРазрядностьОСНеопределена]'
    if (platformType === ComConnection.PlatformType.Windows_x86) {
      platformTypeString = '32 bit'
    }
    if (platformType === ComConnection.PlatformType.Windows_x86_64) {
      platformTypeString = '64 bit'
    }
    log(format('Версия comcntr: {0} {1}', appVersion, platformTypeString), false)
    return ComConnection
  } catch (e) {
    log(format('[СообщениеОтказСоединенияСБазой]', logstep, e.name, e.message), true)
    return null
  }
}

// Записать весь log-файл в журнал регистрации
function write1CEventLog () {
  if (!oFileSystemObject.FileExists(outFileName)) {
    return
  }

  var connection = createConnection()
  if (connection == null) {
    return
  }
  try {
    var f = oFileSystemObject.OpenTextFile(outFileName, ForReading, false, TristateTrue)

    var text
    while (!f.AtEndOfStream) {
      text = f.ReadLine()
      while ((text.indexOf(successMarker) < 0) && (text.indexOf(errorMarker) < 0) && !f.AtEndOfStream) {
        text += '\n' + f.ReadLine()
      }

      var failed = text.indexOf(errorMarker) > 0
      log1CInternal(connection, text, failed)
    }
  } catch (e) {
    log(format('[СообщениеОтказЛогирования1С]', e.name, e.message), true)
    return
  } finally {
    connection = null
  }
}

function doSetResult (success) {
  var connection = createConnection()
  if (connection == null) {
    return (useComConnector ? 1 : 0)
  }
  var res = 0
  try {
    connection.РезервноеКопированиеИБСервер.ЗавершитьВосстановление(success)
  } catch (e) {
    log(format('[СообщениеОтказПриВызовеЗавершитьВосстановление]', e.name, e.message), true)
    res = 2
  }
  return res
}

// Передать в приложение результат выполнения
function setResult (success) {
  var result = doSetResult(success)
  CollectGarbage() // освобождает соединения с COM-объектом
  return result
}

// Записать результат выполнения процедуры обновления в Event Log
function writeEventLog (success) {
  try {
    var eventKind = success ? EVENT_SUCCESS : EVENT_CRITICAL
    var message
    if (success) {
      message = '[СообщениеРезультатВосстановленияБазы]';
    } else {
      message = '[СообщениеОтказВосстановленияБазы]';
    }
    message += format(' [СообщениеПараметрыБазы]', infoBasePath)
    if (!success) {
      message += ' [СообщениеВосстановлениеЛогирование1С]';
    }
    oShell.LogEvent(eventKind, message)
  } catch (e) {
    log(format('[СообщениеОтказЛогирования]', e.name, e.message), true)
  }
}

// Восстановление информационной базы из резервной копии
function restoreDB () {
  // сначала переместим cd-файл базы во временный каталог
  try {
    if (oFileSystemObject.FileExists(TempCatalog + '1Cv8.1CD')) {
      oFileSystemObject.DeleteFile(TempCatalog + '1Cv8.1CD')
    }
    var FileObject = oFileSystemObject.GetFile(BaseFileName + '1Cv8.1CD')
    FileObject.Move(TempCatalog)
  } catch (e) {
    retryCount++
    CollectGarbage() // предотвращает исключение out of memory
    if (retryCount > 5) {
      log(format('[СообщениеОтказПереносаФайлаБазыВоВременныйКаталог]', e.name, e.message), true)
      return 1
    } else {
      log(format('[СообщениеПопыткаПереносаФайлаБазыВоВременныйКаталог]', retryCount, e.name, e.message))
      return -1
    }
  }

  log(format('[СообщениеПутьКФайлуРезервнойКопии]', backupFileName))
  var ret = 0
  try {
    if (backupFileName.toLowerCase().lastIndexOf('.zip') === backupFileName.length - 4) {
      var backupFile = oFileSystemObject.GetAbsolutePathName(backupFileName)
      var backupFolder = oShellApplication.NameSpace(backupFile)
      var dbFile = oFileSystemObject.GetAbsolutePathName(BaseFileName)
      var dbFolder = oShellApplication.Namespace(dbFile)
      dbFolder.CopyHere(backupFolder.Items())
    } else {
      oFileSystemObject.CopyFile(backupFileName, BaseFileName, true)
    }

    log('[СообщениеРезультатВосстановленияБазы]')
  } catch (e) {
    CollectGarbage() // предотвращает исключение out of memory
    log(format('[СообщениеОтказВосстановленияБазыПодробно]', e.name, e.message), true)
    ret = 1
    var FileObject = oFileSystemObject.GetFile(TempCatalog + '1Cv8.1CD')
    FileObject.Move(BaseFileName)
  }
  return ret
}

function WaitUntilFinish () {
  var fileName = BaseFileName + '\\1Cv8.1CD'
  
  if (!oFileSystemObject.FileExists(fileName)) {
    return -1
  }
  
  var fileArchive = oFileSystemObject.GetFile(fileName)
  var fileSize = fileArchive.size / 1024
  if (fileSize < 5) {
    return -1
  }

  return 0
}

function doAllowConnections () {
  var BlockFilePath = BaseFileName + '1Cv8.cdn'
  if (oFileSystemObject.FileExists(BlockFilePath)) {
    try {
      oFileSystemObject.DeleteFile(BaseFileName + '1Cv8.cdn')
    } catch (e) {
      log(format('[СообщениеОшибкаУдаленияФайлаБлокировок]', e.name, e.message), true)
      return 3
    }
  }
  return 0
}

// Первоначальное заполнение информационной базы, разрешение подключений новых соединений,
// и оповещение о результате обновления
function allowConnections () {
  var result = doAllowConnections()
  CollectGarbage() // освобождает соединения с COM-объектом
  return result
}

function fromUnicode (text) {
  var str = ''
  for (var i = 0; i < text.length / 4; i++) {
    str = str + String.fromCharCode(text.slice(4 * i, 4 * i + 4))
  }
  str = str.replace(/"/g, '""')
  return str
}

// Интерактивный запуск "1С:Предприятие"
function runEnterprise () {
  return runApp(
    v8exe,
    format(
      'ENTERPRISE {0} {1} {2}',
      infoBasePath,
      infoBaseAuthorization,
      runAppAdditionalParams),
    SW_SHOW,
    false)
  return 0
}

#КонецОбласти
